#include <all_wlib.h>
#pragma hdrstop
//#pragma package(smart_init)
#pragma link "bklist"
#pragma link "hcomps"
#pragma link "hwindow"
#pragma link "HExcept"
#pragma link "BkList"
#pragma link "HWindow"
#pragma link "HCFGStorage"
#pragma resource "*.dfm"

#include "nv_Main.h"
#pragma link "HMenus"
#pragma link "XPMenu"
#pragma link "TB97Ctls"

TMainForm *MainForm;
PHStorageLink Opt;

static ImportDef defs[] = {
  { "Naviscope log files|browse*.txt",  "Naviscope",    LoadNaviscope },
  { "Squid native log files|access*.*", "Squid native", LoadSquid },
  { "Unix Traf log files|traf*.txt",    "Traf",         LoadTraf }
};

//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
        : TForm(Owner)
  {
    cfg->FileName  = MakeStartLocalPath( "nvlog.hcfg" );

    FSortMode = -1;
    FGroup    = (tpGroups)-1;
    FCollapse = (tpGroups)-1;

    for( int n = 0; n < ARRAY_SIZE(defs); n++ ) {
      TMenuItem *mi;
      mnuLoad->Add( mi = new TMenuItem( mnuLoad ) );
      mi->Caption = Message( "&%c. Load %s files...", '1'+n, defs[n].MenuTitle );
      mi->Tag     = 1000+n;
      mi->OnClick = idLoad;
    }

   //Create cfg link
     Opt = new HStorageLink;
       Opt->Storage = cfg;
       Opt->KeyPath = "IDE";

   //Load cfg
     cfg->Load();

     Aliases.Load();

     HLoadFormPos( Opt->SubKey("MainWindow"), this );
     cbItem->Width     = panTop->Width - (732-386);
     edtFullUrl->Width = panBottom->Width - (732-553);
     edtUrl->Width     = panBottom->Width - (732-165);

#define RD(v) header->Sections->Items[v]->Width = GOPT( "Header" #v ,header->Sections->Items[v]->Width );
    RD( 0 )
    RD( 1 )
    RD( 2 )
    RD( 3 )
    RD( 4 )
#undef RD
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormCreate(TObject *Sender)
  {
   //Setup loaded
InProcess = true;
    GroupItem  = 0;
    SortMode   = GOPT( "SortMode",  0 );
    Group      = (tpGroups)GOPT( "Group",     GRP_NONE );
    Collapse   = (tpGroups)GOPT( "Collapse",  GRP_NONE );
InProcess = false;
    Regroup();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::FormClose(TObject *Sender, TCloseAction &Action)
{
//Save
    HSaveFormPos( Opt->SubKey("MainWindow"), this );

    //Names
    Aliases.Save();

    //Opts
    SOPT( "SortMode",  SortMode );
    SOPT( "Group",     (int)Group );
    SOPT( "Collapse",  (int)Collapse );

#define WR(v) SOPT( "Header" #v ,header->Sections->Items[v]->Width );
    WR( 0 )
    WR( 1 )
    WR( 2 )
    WR( 3 )
    WR( 4 )
#undef WR

//Save
    if ( GOPT("Autosave",true) )
      cfg->Save();

//Delete
    Notification( cfg, opRemove );
    delete Opt; Opt = NULL;
}

void __fastcall TMainForm::idLoad( TObject *Sender )
  {  PImportDef p = &defs[ ((TMenuItem*)Sender)->Tag-1000 ];

     open->InitialDir = GOPT( "LastUsedPath", MakeStartLocalPath("").Text() );
     open->Filter     = AnsiString(p->Filter) + "|All files|*.*";
     if ( !open->Execute() ) return;
     SOPT( "LastUsedPath", FPath( open->FileName.c_str() ) );

     HOperation rop(this);
     PRPeriod   h( 700 );

     FullItems.Sort( idSortLoad );
     p->Proc( Handle, rop, h, open->FileName.c_str(), idAddUrlItem );

     Group = Group;
}

bool __fastcall TMainForm::idAddUrlItem( const UrlInfo& it )
  {  PNames p;
     int    num, pos;

     num = FullItems.Search( it, idSortLoad, &pos );
     if ( num == -1 )
       FullItems.AddAt( pos, it );
      else {
        PUrlInfo ui = FullItems[ num ];
        ui->Size += it.Size;
      }

     //Add site group
     p = (PNames)Sites.Locate( (HANDLE)it.Site.Text() );
     if (p) {
       p->Object.Count++;
       p->Object.Size += it.Size;
     } else {
       p = (PNames)Sites.Add( (HANDLE)it.Site.Text() );
       p->Object.Count = 1;
       p->Object.Size = it.Size;
     }

     //Add host group
     p = (PNames)Hosts.Locate( (HANDLE)it.Host.Text() );
     if (p) {
       p->Object.Count++;
       p->Object.Size += it.Size;
     } else {
       p = (PNames)Hosts.Add( (HANDLE)it.Host.Text() );
       p->Object.Count = 1;
       p->Object.Size = it.Size;
     }

     //Add time group
     char *m = it.Time.GetStr(NULL,0,"%d-%m-%Y");
     p = (PNames)Dates.Locate( (HANDLE)m );
     if (p) {
       p->Object.Count++;
       p->Object.Size += it.Size;
     } else {
       p = (PNames)Dates.Add( (HANDLE)m );
       p->Object.Count = 1;
       p->Object.Size = it.Size;
     }
     
 return true;
}

//---------------------------------------------------------------------------
void TMainForm::SplitFiles( int interval )
  {
     SplitInterval = interval;

   //Source file
     open->InitialDir = GOPT( "LastUsedPath", MakeStartLocalPath("").Text() );
     open->Filter     = "";
     for( int n = 0; n < ARRAY_SIZE(defs); n++ ) {
       if ( open->Filter.Length() ) open->Filter = open->Filter + "|";
       open->Filter = open->Filter + defs[n].Filter;
     }
     open->FilterIndex = GOPT( "LastFilterIndex", 1 );
     if ( !open->Execute() ) return;
     SOPT( "LastUsedPath", FPath( open->FileName.c_str() ) );
     SOPT( "LastFilterIndex", open->FilterIndex );

   //Dest directory
     save->InitialDir = GOPT( "LastSavePath", MakeStartLocalPath("").Text() );
     if ( !save->Execute() ) return;
     SOPT( "LastSavePath", FPath( save->FileName.c_str() ) );

     char *m;

     TStrCpy( SplitPath, save->FileName.c_str() );
     m = strrchr(SplitPath,'\\');
     if (m) *m = 0; else { SplitPath[0] = '.'; SplitPath[1] = 0; }

   //Do split
     HOperation rop(this);
     PRPeriod   h( 700 );

     SplitAskFile = true;
     defs[ open->FilterIndex-1 ].Proc( Handle, rop, h, open->FileName.c_str(), idSplitFile );

     if ( Files.Count() &&
          AskYesNo( Handle, NULL,
                    "File \"%s\" processed successfully.\n"
                    "Created %d output files in \"%s\" directory.\n"
                    "\n"
                    "Do you wish to delete source file?",
                    open->FileName.c_str(), Files.Count(), SplitPath ) )
       if ( !DeleteFile( open->FileName.c_str() ) )
         AskMessage( Handle, NULL,
                     "Error delete file:\nFile: \"%s\"\nError: %s",
                     open->FileName.c_str(), FIO_ERROR );

     Files.DeleteAll();
}

//---------------------------------------------------------------------------
DECL_PROP_SET( TMainForm, tpGroups, Collapse )
  {
    if ( FCollapse == val ) return;

//Resert sort method if collapsed
    if ( val && SortMode && SortMode != SRT_SIZE && (val-1)*2+1 == Abs(SortMode) )
      SortMode = 0;

//Set
    FCollapse = val;
    cbCollapse->ItemIndex = val;
    Regroup();
}

static int RTL_CALLBACK idFilterCBSort( const PNames *left,const PNames *right )
  {
     if ( MainForm->Group == GRP_DATE )
       return StrCmp( (*left)->Name.Text(), (*right)->Name.Text() );
      else
       return -( (*left)->Object.Size - (*right)->Object.Size );
}

DECL_PROP_SET( TMainForm, tpGroups, Group )
  {  bool isNew = FGroup != val;

    FGroup = val;
    cbGroup->ItemIndex = val;
    txtFilterItem->Visible = val != 0;
    cbItem->Visible        = val != 0;

    cbItem->Clear();
    cbItem->Items->Add( "<do not filter>" );

    FilterItems.DeleteAll();
    CurNames()->EnumCL( idFillItems );
    FilterItems.Sort( idFilterCBSort );
    
    for( int n = 0; n < FilterItems.Count(); n++ ) {
      PNames p = FilterItems[ n ];
      cbItem->Items->AddObject( p->Name, (TObject*)p );
    }

    if (isNew || cbItem->ItemIndex != GroupItem ) GroupItem = 0;
}

DECL_PROP_SET( TMainForm, int, GroupItem )
  {
    FGroupItem = val;
    cbItem->ItemIndex = val;
    Regroup();
}

DECL_PROP_SET( TMainForm, int, SortMode )
  {
    if ( FSortMode == val ) return;

    if ( FSortMode != 0 )
      header->Sections->Items[ Abs(FSortMode)-1 ]->ImageIndex = -1;

    FSortMode = val;

    if ( FSortMode != 0 )
      header->Sections->Items[ Abs(FSortMode)-1 ]->ImageIndex = (FSortMode>0)?0:1;

    Resort();
}
//---------------------------------------------------------------------------
BOOL __fastcall TMainForm::idFillItems( PHBTreeBase tree,int Level )
  {
    FilterItems.Add( (PNames)tree );
 return TRUE;
}
PNames TMainForm::CurNames( int num )
  {
    if ( num == -1 )
      num = cbGroup->ItemIndex;

    if ( num != 0 )
      switch( num ) {
        case GRP_SITE: return &Sites;
        case GRP_ADDR: return &Hosts;
        case GRP_DATE: return &Dates;
      }
      
 return &NullNames;
}

void TMainForm::Regroup( void )
  {  int  item   = (Group && GroupItem) ? cbItem->ItemIndex : (-1);
     bool inItem = item > 0 && item < cbItem->Items->Count;
     int  cn = 0;

     if ( InProcess ) return;
     InProcess = true;
     Items.DeleteAll();

//Need to filter
     if ( inItem || Collapse ) {
       HOperation    rop(this);
       PRPeriod      h( 700 );
       UrlInfo       it,*p;
       CollapseNames names;
       int           cmp;

       rop.SetAbort(true);

//Filtered item
       if ( inItem ) {
         PNames nm = (PNames)cbItem->Items->Objects[GroupItem];
         it.Set( Group, nm->Name.Text() );
       }

//For each in FullList
       for ( int n = 0; n < FullItems.Count(); n++ ) {
         p = FullItems[n];

         //Check abort
         ProcessMessages();
         if ( rop.isAborted() &&
              AskYesNo( Handle,"Terminate filtering process ?" ) ) {
           GroupItem = 0;
           InProcess = false;
           return;
         }

         //Refresh info
         if ( h.End() ) {
           rop.SetProgress( 100. * (n+1) / FullItems.Count() );
           rop.SetText( "Filtering...\n%3d%% Checked: %d Used: %d",
                        (n+1)*100/FullItems.Count(), n+1, Items.Count() );
         }

         //Compare with filter
         if ( inItem ) {
           switch( Group ) {
             case GRP_SITE: cmp = StrCmpI( p->Site.Text(),it.Site.Text() ); break;
             case GRP_ADDR: cmp = StrCmpI( p->Host.Text(),it.Host.Text() ); break;
             case GRP_DATE: cmp = it.CmpTime( *p, true ); break;
                   default: cmp = 1;
           }
         } else
           cmp = 0;

         //Do not fit into filter
         if ( cmp != 0 ) continue;

         //Add to items
         cn++;
         if ( !Collapse ) {
           Items.Add( *p );
           continue;
         }

         /*COLLAPSE*/{
           PCollapseNames nm;
           CONSTSTR       m = p->TypeStr( Collapse ),
                          tmp;
           Assert( m );

           //Lookup
           nm = (PCollapseNames)names.Locate( (HANDLE)m );

           //Exist
           if ( nm ) {
             //Add size
             nm->Object->Size += p->Size;

             //Add site
             tmp = p->Site.Text();
             if ( nm->Object->Site.Length() < SITE_STRING_LEN && strstr(nm->Object->Site.Text(),tmp) == NULL ) {
               if ( nm->Object->Site.Length() ) nm->Object->Site.Add( ", " );
               nm->Object->Site.Add( tmp );
             }

             //Add host
             tmp = p->Host.c_str();
             if ( nm->Object->Host.Length() < HOST_STRING_LEN && strstr(nm->Object->Host.Text(),tmp) == NULL ) {
               if ( nm->Object->Host.Length() ) nm->Object->Host.Add( ", " );
               nm->Object->Host.Add( tmp );
             }

             //Add url
             tmp = p->Url.c_str();
             if ( nm->Object->Url.Length() < URL_STRING_LEN && strstr(nm->Object->Url.Text(),tmp) == NULL ) {
               if ( nm->Object->Url.Length() ) nm->Object->Url.Add( "\r\n" );
               nm->Object->Url.Add( tmp );
             }
             continue;
           }

           //Add new
           nm = (PCollapseNames)names.Add( (HANDLE)m );
           nm->Object = Items.Add( *p );
         }/*COLLAPSE*/
       }
       rop.SetText( NULL );
     }

//Correct items count
     list->ItemsCount = CurItems->Count();

//Resort
     InProcess = false;
     Resort();
}

void TMainForm::Resort( void )
  {
     if (InProcess) return;

     PInfoArray p = CurItems;
     HOperation rop(this);

     if ( SortMode ) {
       rop.SetText( "Sorting..." );
       switch( SortMode ) {
         case   SRT_SITE: p->Sort( idSortSite ); break;
         case   SRT_ADDR: p->Sort( idSortAddr ); break;
         case   SRT_SIZE: p->Sort( idSortSize ); break;
         case   SRT_DATE: p->Sort( idSortDate ); break;
         case SRT_SITE_I: p->Sort( idSortSiteI ); break;
         case SRT_ADDR_I: p->Sort( idSortAddrI ); break;
         case SRT_SIZE_I: p->Sort( idSortSizeI ); break;
         case SRT_DATE_I: p->Sort( idSortDateI ); break;
       }
     }

     __int64 cn = 0;
     char    dig[ 20 ];

     rop.SetText( "Calculating..." );
     CurItems->TotalSize(&cn);

     txtCount->Caption    = FullItems.Count();
     txtFiltered->Caption = CurItems->Count();
     txtSize->Caption     = MakeIntString( Digit2Str( cn, dig, 10, sizeof(dig) ), ',' );

     list->Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::cbGroupChange(TObject *Sender)
  {
     Group = (tpGroups)cbGroup->ItemIndex;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::cbItemChange(TObject *Sender)
  {
     GroupItem = cbItem->ItemIndex;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::headerSectionClick( THeaderControl *HeaderControl, THeaderSection *Section)
  {  int num = Section->Index+1;

//In collapse mode can sort only by collapse criterio
     if ( Collapse && Section->Index+1 != SRT_SIZE && (Collapse-1)*2 != Section->Index)
       return;

//Change sort: NONE -> ASC -> DESC -> NONE
     if ( Abs(SortMode) == num ) {
       if (SortMode > 0 )
         SortMode = -SortMode;
        else
         SortMode = 0;
     } else
       SortMode = num;
}
//---------------------------------------------------------------------------
bool __fastcall TMainForm::listDrawItemSection(TObject *Sender,
      TCanvas *dc, int Index, THeaderSection *Section, const MyRect &Rect,
      const TOwnerDrawState &State)
  {  UINT     al;
     PUrlInfo p;
     CONSTSTR txt;
     char     dig[ 20 ];

     if ( (p=CurItems->Item(Index)) == NULL )
       return false;

     switch( Section->Index+1 ) {
       case SRT_SITE: al = DT_LEFT;  txt = p->Site.Text(); break;
       case  SRT_URL: al = DT_LEFT;  txt = Collapse ? "<ALL>" : p->Url.Text();  break;
       case SRT_ADDR: al = DT_LEFT;  txt = Aliases.DeName(p->Host.Text()); break;
       case SRT_SIZE: al = DT_RIGHT; txt = MakeIntString( Digit2Str( p->Size, dig, 10, sizeof(dig) ), ',' ); break;
       case SRT_DATE: al = DT_LEFT;  txt = p->Time.GetStr(NULL,-1); break;
             default: txt = "";
     }

    TC_TextOut( dc,Rect.Inflated(-5,0),txt,al | DT_VCENTER | DT_SINGLELINE );
 return true;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::idClear(TObject *Sender)
  {
     if ( !FullItems.Count() ||
          !AskYesNo( Handle, NULL,
                     "Clear list of %d entries?",
                     FullItems.Count() ) )
       return;

     Items.DeleteAll();
     FullItems.DeleteAll();
     Sites.Clear();
     Hosts.Clear();
     Dates.Clear();
     Regroup();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::cbItemDrawItem(TWinControl *Control,int Index, TRect &Rect, TOwnerDrawState State)
  {  PNames   nm = (PNames)cbItem->Items->Objects[Index];
     MyRect   r;
     TCanvas *dc = cbItem->Canvas;
     char     dig[ 20 ];

     static int pos[] = { 2,-250,-150,-2 };

     dc->FillRect( Rect );
     SetBkMode( dc->Handle,TRANSPARENT );

     if ( nm ) {
      #define POS( v ) (pos[v]>0) ? (Rect.Left+pos[v]) : (Rect.Right+pos[v])
       r.Set( POS(0),Rect.Top+1,POS(1)-5,Rect.Bottom-1 );
       TC_TextOut( dc, r,
                   (Group==GRP_ADDR) ? Aliases.DeName(nm->Name.Text()) : nm->Name.Text(),
                   DT_VCENTER|DT_SINGLELINE|DT_LEFT );

       r.Set( POS(1),Rect.Top+1,POS(2)-5,Rect.Bottom-1 );
       TC_TextOut( dc, r,
                   "Visited:",
                   DT_VCENTER|DT_SINGLELINE|DT_LEFT );
       dc->Font->Style = TFontStyles() << fsBold;
       TC_TextOut( dc, r,
                   MakeIntString( Digit2Str( nm->Object.Count, dig, 10, sizeof(dig) ), ',' ),
                   DT_VCENTER|DT_SINGLELINE|DT_RIGHT );
       dc->Font->Style = TFontStyles();

       r.Set( POS(2),Rect.Top+1,POS(3)-5,Rect.Bottom-1 );
       TC_TextOut( dc, r,
                   "Loaded:",
                   DT_VCENTER|DT_SINGLELINE|DT_LEFT );
       dc->Font->Style = TFontStyles() << fsBold;
       TC_TextOut( dc, r,
                   MakeIntString( Digit2Str( nm->Object.Size, dig, 10, sizeof(dig) ), ',' ),
                   DT_VCENTER|DT_SINGLELINE|DT_RIGHT);
       dc->Font->Style = TFontStyles();
      #undef POS
     } else
       TC_TextOut( dc, Rect,
                   cbItem->Items->Strings[Index].c_str(),
                   DT_VCENTER|DT_SINGLELINE|DT_LEFT );
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::cbCollapseChange(TObject *Sender)
  {
     Collapse = (tpGroups)cbCollapse->ItemIndex;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ppSiteListPopup(TObject *Sender)
  {  PUrlInfo p = CurItems->Item(list->ItemIndex);

    mnuFilter->Items[GRP_SITE-1]->Enabled = p && (!Collapse || Collapse == GRP_SITE);
    mnuFilter->Items[GRP_ADDR-1]->Enabled = p && (!Collapse || Collapse == GRP_ADDR);
    mnuFilter->Items[GRP_DATE-1]->Enabled = p && (!Collapse || Collapse == GRP_DATE);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::idSetFilter(TObject *Sender)
  {  PUrlInfo    p = CurItems->Item(list->ItemIndex);
     TComponent *c = dynamic_cast<TComponent*>(Sender);

     if ( !p || !c->Tag ) return;

     InProcess = true;
       Group     = (tpGroups)c->Tag;
       GroupItem = cbItem->Items->IndexOf( p->TypeStr( Group ) );
     InProcess = false;
     Regroup();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::listClick(TObject *Sender)
  {  PUrlInfo    p = CurItems->Item(list->ItemIndex);

     if ( !p || isCollapsed )
       edtFullUrl->Text = "";
      else
       edtFullUrl->Text = p->Site + "/" + p->Url.Text();

     edtSite->Text = p ? p->Site.Text() : "";
     edtHost->Text = p ? Aliases.DeName( p->Host.Text() ) : "";
     edtUrl->Text  = p ? p->Url.Text() : "";
     edtSize->Text = p ? p->Size : 0;
     edtTime->Text = p ? p->Time.GetStr(NULL,-1) : "";
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::idShowNames(TObject *Sender)
  {
     SOPT( "UseNames", !GOPT( "UseNames", false ) );
     list->Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::idNamesEdit(TObject *Sender)
  {
    EditNames( Aliases );
    list->Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::idConfigure(TObject *Sender)
  {
    Configure();
    list->Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::HMainMenu1MenuItemCheck(TObject *Sender, TMenuItem *mi )
  {
     switch( mi->Tag ) {
       case 100: //Show names
                 mi->Checked = GOPT( "UseNames",false );
             break;
     }
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::M1Click(TObject *Sender) { SplitFiles( 33 ); }
void __fastcall TMainForm::D1Click(TObject *Sender) { SplitFiles( 10 ); }
void __fastcall TMainForm::D2Click(TObject *Sender) { SplitFiles( 1 ); }
void __fastcall TMainForm::N1Click(TObject *Sender) { SplitFiles( 7 ); }
//---------------------------------------------------------------------------

